#pragma once

#include <fstream>
#include <vector>
#include <string>
#include <cstring>
#include <map>
#include <list>
#include <set>

#include <utility>
#include <sys/stat.h> //to get size of file

#include "Serializable.h"

using namespace std;

namespace Serialize
{

    class DataStream
    {
    private:
        std::vector<char> m_buf;
        int m_pos;             
    private:
        void reserve(int len);

    public:
        enum DataType
        {
            BOOL = 0,
            CHAR,
            INT32,
            INT64,
            FLOAT,
            DOUBLE,
            STRING,
            VECTOR,
            LIST,
            SET,
            MAP,
            PAIR,
            CUSTOM
        };

        DataStream() : m_pos(0) {}
        ~DataStream() {}

        void save_to_file(const char *fileName);
        bool load(const char *fileName);

        void write(const char *data, int len); // 底层写入函数
        void write(bool data);
        void write(char data);
        void write(int32_t data);
        void write(int64_t data);
        void write(float data);
        void write(double data);
        void write(const char *data);
        void write(const string &data);
        void write(const Serializable &value);

        template <typename T>
        void write(const std::vector<T> &value);
        template <typename T>
        void write(const std::list<T> &value);
        template <typename T>
        void write(const std::set<T> &value);
        template <typename K, typename V>
        void write(const std::map<K, V> &value);
        template <typename F, typename S>
        void write(const std::pair<F, S> &value);
        template <typename T, typename... Args> // 可变参数序列化
        void write_args(const T &value, const Args &...args);
        void write_args();

        bool read(char *data, int len);
        bool read(bool &value);
        bool read(char &value);
        bool read(int32_t &value);
        bool read(int64_t &value);
        bool read(float &value);
        bool read(double &value);
        bool read(string &value);
        bool read(Serializable &value);

        template <typename T>
        bool read(std::vector<T> &value);
        template <typename T>
        bool read(std::list<T> &value);
        template <typename T>
        bool read(std::set<T> &value);
        template <typename K, typename V>
        bool read(std::map<K, V> &value);
        template <typename F, typename S>
        bool read(std::pair<F, S> &value);
        template <typename T, typename... Args> // 可变参数反序列化
        bool read_args(T &value, Args &...args);
        bool read_args();
        // 重载输入
        template <typename T>
        DataStream &operator<<(T value);

        template <typename T>
        DataStream &operator<<(const vector<T> &value);
        template <typename T>
        DataStream &operator<<(const list<T> &value);
        template <typename T, typename K>
        DataStream &operator<<(const map<T, K> &value);
        template <typename T>
        DataStream &operator<<(const set<T> &value);
        // 重载输出
        template <typename T>
        DataStream &operator>>(T &value);

        template <typename T>
        DataStream &operator>>(const vector<T> &value);
        template <typename T>
        DataStream &operator>>(const list<T> &value);
        template <typename T, typename K>
        DataStream &operator>>(const map<T, K> &value);
        template <typename T>
        DataStream &operator>>(const set<T> &value);
    };

    void DataStream::reserve(int len)
    {
        int size = m_buf.size();
        int cap = m_buf.capacity();
        if (size + len > cap)
        {
            while (size + len > cap)
            {
                if (cap == 0)
                {
                    cap = 1;
                }
                else
                {
                    cap *= 2;
                }
            }
            m_buf.reserve(cap);
        }
    }

    void DataStream::write(const char *data, int len)
    {
        reserve(len);
        int size = m_buf.size();
        m_buf.resize(size + len);
        std::memcpy(&m_buf[size], data, len);
    }

    void DataStream::write(bool data)
    {
        char type = DataStream::BOOL;
        write((char *)&type, sizeof(char));
        write((char *)&data, sizeof(char));
    }

    void DataStream::write(char data)
    {
        char type = DataStream::CHAR;
        write((char *)&type, sizeof(char));
        write((char *)&data, sizeof(char));
    }

    void DataStream::write(int32_t data)
    {
        char type = DataStream::INT32;
        write((char *)&type, sizeof(char));
        write((char *)&data, sizeof(int32_t));
    }

    void DataStream::write(int64_t data)
    {
        char type = DataStream::INT64;
        write((char *)&type, sizeof(char));
        write((char *)&data, sizeof(int64_t));
    }

    void DataStream::write(float data)
    {
        char type = DataStream::FLOAT;
        write((char *)&type, sizeof(char));
        write((char *)&data, sizeof(float));
    }

    void DataStream::write(double data)
    {
        char type = DataStream::DOUBLE;
        write((char *)&type, sizeof(char));
        write((char *)&data, sizeof(double));
    }

    void DataStream::write(const char *data)
    {
        char type = DataStream::STRING;
        write((char *)&type, sizeof(char));
        int len = strlen(data);
        write(len);
        write(data, len);
    }

    void DataStream::write(const string &data)
    {
        char type = DataStream::STRING;
        write((char *)&type, sizeof(char));
        int len = data.size();
        write(len);
        write(data.data(), len);
    }

    void DataStream::write(const Serializable &value)
    {
        value.serialize(*this);
    }

    template <typename T>
    void DataStream::write(const std::vector<T> &value)
    {
        char type = DataType::VECTOR;
        write((char *)&type, sizeof(char));
        int len = value.size();
        write(len);
        for (int i = 0; i < len; i++)
        {
            write(value[i]);
        }
    }

    template <typename T>
    void DataStream::write(const std::list<T> &value)
    {
        char type = DataType::VECTOR;
        write((char *)&type, sizeof(char));
        int len = value.size();
        write(len);
        for (auto it = value.begin(); it != value.end(); it++)
        {
            write(*it);
        }
    }

    template <typename T>
    void DataStream::write(const std::set<T> &value)
    {
        char type = DataType::SET;
        write((char *)&type, sizeof(char));
        int len = value.size();
        write(len);
        for (auto it = value.begin(); it != value.end(); it++)
        {
            write(*it);
        }
    }

    template <typename K, typename V>
    void DataStream::write(const std::map<K, V> &value)
    {
        char type = DataType::MAP;
        write((char *)&type, sizeof(char));
        int len = value.size();
        write(len);
        for (auto it = value.begin(); it != value.end(); it++)
        {
            write(it->first);
            write(it->second);
        }
    }

    template <typename F, typename S>
    void DataStream::write(const std::pair<F, S> &value)
    {
        char type = DataType::PAIR;
        write((char *)&type, sizeof(char));
        write(value.first);
        write(value.second);
    }

    template <typename T, typename... Args>
    void DataStream::write_args(const T &value, const Args &...args)
    {
        write(value);
        write_args(args...);
    }

    void DataStream::write_args()
    {
    }

    bool DataStream::read(char *data, int len)
    {
        std::memcpy(data, (char *)&m_buf[m_pos], len);
        m_pos += len;
        return true;
    }

    bool DataStream::read(bool &value)
    {
        if (m_buf[m_pos] != DataType::BOOL)
        {
            return false; // fail to read
        }
        value = m_buf[++m_pos];
        ++m_pos;
        return true;
    }

    bool DataStream::read(char &value)
    {
        if (m_buf[m_pos] != DataType::CHAR)
        {
            return false; // fail to read
        }
        value = m_buf[++m_pos];
        ++m_pos;
        return true;
    }

    bool DataStream::read(int32_t &value)
    {
        if (m_buf[m_pos] != DataType::INT32)
        {
            return false; // fail to read
        }
        value = *((int32_t *)(&m_buf[++m_pos]));
        m_pos += 4;
        return true;
    }

    bool DataStream::read(int64_t &value)
    {
        if (m_buf[m_pos] != DataType::INT64)
        {
            return false; // fail to read
        }
        value = *((int64_t *)(&m_buf[++m_pos]));
        m_pos += 8;
        return true;
    }

    bool DataStream::read(float &value)
    {
        if (m_buf[m_pos] != DataType::FLOAT)
        {
            return false; // fail to read
        }
        value = *((float *)(&m_buf[++m_pos]));
        m_pos += 4;
        return true;
    }

    bool DataStream::read(double &value)
    {
        if (m_buf[m_pos] != DataType::DOUBLE)
        {
            return false; // fail to read
        }
        value = *((double *)(&m_buf[++m_pos]));
        m_pos += 8;
        return true;
    }

    bool DataStream::read(string &value)
    {
        if (m_buf[m_pos] != DataType::STRING)
        {
            return false; // fail to read
        }
        ++m_pos;
        int len;
        read(len);
        if (len < 0)
        {
            return false;
        }
        value.assign((char *)&(m_buf[m_pos]), len);
        m_pos += len;
        return true;
    }

    bool DataStream::read(Serializable &value)
    {
        value.deserialize(*this);
        return true;
    }

    template <typename T>
    bool DataStream::read(std::vector<T> &value)
    {
        value.clear();
        if (m_buf[m_pos] != DataStream::VECTOR)
        {
            return false;
        }
        ++m_pos;
        int len;
        read(len);
        for (int i = 0; i < len; i++)
        {
            T v;
            read(v);
            value.push_back(v);
        }
        return true;
    }

    template <typename T>
    bool DataStream::read(std::list<T> &value)
    {
        value.clear();
        if (m_buf[m_pos] != DataStream::LIST)
        {
            return false;
        }
        ++m_pos;
        int len;
        read(len);
        for (int i = 0; i < len; i++)
        {
            T v;
            read(v);
            value.push_back(v);
        }
        return true;
    }

    template <typename T>
    bool DataStream::read(std::set<T> &value)
    {
        value.clear();
        if (m_buf[m_pos] != DataStream::SET)
        {
            return false;
        }
        ++m_pos;
        int len;
        read(len);
        for (int i = 0; i < len; i++)
        {
            T v;
            read(v);
            value.insert(v);
        }
        return true;
    }

    template <typename K, typename V>
    bool DataStream::read(std::map<K, V> &value)
    {
        value.clear();
        if (m_buf[m_pos] != DataStream::MAP)
        {
            return false;
        }
        ++m_pos;
        int len;
        read(len);
        for (int i = 0; i < len; i++)
        {
            K k;
            read(k);
            V v;
            read(v);
            value[k] = v;
        }
        return true;
    }

    template <typename F, typename S>
    bool DataStream::read(std::pair<F, S> &value)
    {
        if (m_buf[m_pos] != DataStream::PAIR)
        {
            return false;
        }
        ++m_pos;
        read(value.first);
        read(value.second);
        return true;
    }

    template <typename T, typename... Args>
    bool DataStream::read_args(T &value, Args &...args)
    {
        read(value);
        return read_args(args...);
    }

    bool DataStream::read_args()
    {
        return true;
    }

    template <typename T>
    DataStream &DataStream::operator<<(T value)
    {
        write(value);
        return *this;
    }

    template <typename T>
    DataStream &DataStream::operator<<(const vector<T> &value)
    {
        write(value);
        return *this;
    }
    template <typename T>
    DataStream &DataStream::operator<<(const list<T> &value)
    {
        write(value);
        return *this;
    }
    template <typename T, typename K>
    DataStream &DataStream::operator<<(const map<T, K> &value)
    {
        write(value);
        return *this;
    }
    template <typename T>
    DataStream &DataStream::operator<<(const set<T> &value)
    {
        write(value);
        return *this;
    }

    template <typename T>
    DataStream &DataStream::operator>>(T &value)
    {
        read(value);
        return *this;
    }

    template <typename T>
    DataStream &DataStream::operator>>(const vector<T> &value)
    {
        read(value);
        return *this;
    }
    template <typename T>
    DataStream &DataStream::operator>>(const list<T> &value)
    {
        read(value);
        return *this;
    }
    template <typename T, typename K>
    DataStream &DataStream::operator>>(const map<T, K> &value)
    {
        read(value);
        return *this;
    }
    template <typename T>
    DataStream &DataStream::operator>>(const set<T> &value)
    {
        read(value);
        return *this;
    }

    void DataStream::save_to_file(const char *fileName)
    {
        ofstream os(fileName, ios::binary);
        if (os.is_open())
        {
            os.write(&m_buf[0], m_buf.size());
        }
        else
        {
            std::cout << "The file can not be opened!" << std::endl;
        }
        os.close();
    }

    bool DataStream::load(const char *fileName)
    {
        ifstream in(fileName, ios::binary);
        if (in.is_open())
        {
            struct stat info;
            stat(fileName, &info);
            int fileSize = info.st_size;
            if (fileSize != 0)
            {
                m_buf.clear();
                m_buf.resize(fileSize);
                in.read(&m_buf[0], fileSize);
            }
        }
        else
        {
            std::cout << "Can't open file" << std::endl;
        }
        in.close();
        return 0;
    }

    template <typename T>
    void serialize(T data, const char *fileName)
    {
        DataStream ds;
        ds << data;
        ds.save_to_file(fileName);
    }

    template<typename T>
    void deserialize(T &data, const char *filename)
    {
        DataStream ds;
        ds.load(filename);
        ds >> data;
    }
}
